﻿using GenericRepositoryDemo.Domain.Abstracts.Entites;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

namespace GenericRepositoryDemo.Domain.Abstracts.Repositories;

public abstract class RepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey>
    where TEntity : class, IEntity<TKey>
{
    public virtual async Task<TEntity> GetAsync(TKey id)
    {
        var entity = await FindAsync(id);

        if (entity == null)
        {
            throw new EntityNotFoundException(typeof(TEntity), id);
        }

        return entity;
    }
    public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
    {
        var entity = await FindAsync(predicate);

        if (entity == null)
        {
            throw new EntityNotFoundException(typeof(TEntity));
        }

        return entity;
    }
    public abstract Task<TEntity> FindAsync(TKey id);
    public abstract Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate);
    public abstract Task<List<TEntity>> GetListAsync();
    public abstract Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate);
    public abstract Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting);
    public abstract Task<long> GetCountAsync();

    public abstract Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false);
    public virtual async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false)
    {
        foreach (var entity in entities)
        {
            await InsertAsync(entity);
        }

        if (autoSave)
        {
            await SaveChangesAsync();
        }
    }

    public abstract Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false);
    public virtual async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false)
    {
        foreach (var entity in entities)
        {
            await UpdateAsync(entity);
        }

        if (autoSave)
        {
            await SaveChangesAsync();
        }
    }

    public virtual async Task DeleteAsync(TKey id, bool autoSave = false)
    {
        var entity = await FindAsync(id);
        if (entity == null)
        {
            return;
        }

        await DeleteAsync(entity, autoSave);
    }
    public abstract Task DeleteAsync(TEntity entity, bool autoSave = false);
    public abstract Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false);
    public virtual async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false)
    {
        foreach (var entity in entities)
        {
            await DeleteAsync(entity);
        }

        if (autoSave)
        {
            await SaveChangesAsync();
        }
    }
    public virtual async Task DeleteManyAsync([NotNull] IEnumerable<TKey> ids, bool autoSave = false)
    {
        foreach (var id in ids)
        {
            await DeleteAsync(id);
        }

        if (autoSave)
        {
            await SaveChangesAsync();
        }
    }

    #region Common
    public virtual Task SaveChangesAsync()
    {
        return Task.CompletedTask;
    }

    public virtual Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
    {
        return GetQueryableAsync();
    }

    public abstract Task<IQueryable<TEntity>> GetQueryableAsync();
    #endregion
}